/*
 * DiagramPanel.java
 *
 * Created on 13 maart 2008, 14:35
 *
 * @author wijnand.schepens@hogent.be
 */
package diagram;

import data.PredefinedObjects;
import diagram.connections.Connection;
import diagram.handles.ConnectHandle;
import model.DiagramModel;
import diagram.handles.Handle;
import diagram.handles.TriggerHandle;
import diagram.locations.AbsoluteLocation;
import listeners.PopupListener;
import gui.DiagramPopup;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Observable;
import java.util.Observer;
import javax.imageio.*;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import listeners.DropListener;
import listeners.TextFieldListener;

public class DiagramPanel extends JPanel implements MouseListener, MouseMotionListener, Observer {

    private DiagramModel model;
    private Point dragStartPoint = null;
    private Point dragEndPoint = null;
    private Rectangle dragRectangle = null;
    private boolean draggingNode = false;
    private boolean draggingHandle = false;
    private DiagramPopup popup;
    private PredefinedObjects objects;
    private JTextField editField;

    public DiagramPanel(DiagramModel model) {
        this.addMouseListener(this);
        this.addMouseMotionListener(this);
        this.setOpaque(false);
        this.model = model;
        popup = new DiagramPopup(model);
        PopupListener pl = new PopupListener(popup, model);
        objects = new PredefinedObjects();
        this.addMouseListener(pl);
        DropListener target = new DropListener(this, model);
    //this.setDropTarget(target);

    }

    public Dimension getPreferredSize() {
        return new Dimension(500, 500);
    }

    //source = http://www.daniweb.com/forums/thread136630.html
    private BufferedImage toBufferedImage(Image image) {
        if (image instanceof BufferedImage) {
            return (BufferedImage) image;
        }
        image = new ImageIcon(image).getImage();

        BufferedImage bimage = null;
        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
        try {
            int transparency = Transparency.OPAQUE;
            GraphicsDevice gs = ge.getDefaultScreenDevice();
            GraphicsConfiguration gc = gs.getDefaultConfiguration();
            bimage = gc.createCompatibleImage(
                    image.getWidth(null), image.getHeight(null), transparency);
        } catch (HeadlessException e) {
        }

        if (bimage == null) {
            int type = BufferedImage.TYPE_INT_RGB;
            bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
        }
        Graphics g = bimage.createGraphics();
        g.drawImage(image, 0, 0, image.getWidth(null), image.getHeight(null), null);
        g.dispose();
        return bimage;
    }

    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics2D g2 = (Graphics2D) g;
        for (Graphical c : model.getObjects("all")) {
            c.paint(g2);
        }


        if (dragRectangle != null) {
            g2.setColor(Color.RED);
            Stroke oldStroke = g2.getStroke();
            g2.setStroke(objects.getDashedStroke());
            g2.draw(dragRectangle);
            g2.setStroke(oldStroke);
        }
    }

    public void saveJPG() {
        Image img = createImage(getWidth(), getHeight());
        Graphics g = img.getGraphics();
        paint(g);
        File saveFile = new File("tttaaa.jpg");
        JFileChooser chooser = new JFileChooser();
        chooser.setSelectedFile(saveFile);
        int rval = chooser.showSaveDialog(this);
        if (rval == JFileChooser.APPROVE_OPTION) {
            saveFile = chooser.getSelectedFile();
            try {

                ImageIO.write(toBufferedImage(img), "jpg", saveFile);
                JOptionPane.showMessageDialog(null, "Image saved to " + saveFile.toString());
                g.dispose();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    public void selectGraphical(Graphical g, boolean b) {
        for (Graphical gr : model.getObjects("all")) {
            if (g.equals(gr)) {
                model.setGraphicalSelected(gr, b);
            }
        }
    }

     public void deSelect() {
        for (Graphical gr : model.getObjects("all")) {
            model.setGraphicalSelected(gr, false);
        }
    }

    public void mouseClicked(MouseEvent e) {
        System.out.println("MouseClicked");
        //deSelect();
        if (e.isControlDown() && e.getClickCount() == 1) {
            System.out.println("CONTROL");
            for (Graphical g : model.getObjects("node")) {
                Node n = (Node)g;
                if (n.contains(e.getX(), e.getY())) {
                    draggingNode = true;
                    selectGraphical(n, true);
                }
            }
        } else if (!e.isControlDown() && e.getClickCount() == 1) {
            deSelect();
            System.out.println("GEEN CONTROL");
            for (Graphical g : model.getObjects("node")) {
                Node n = (Node)g;
                if (n.contains(e.getX(), e.getY())) {
                    draggingNode = true;
                    selectGraphical(n, true);
                }
            }
        } else if (e.getClickCount() == 2) {
            System.out.println("double clicked");
            for (Graphical g : model.getObjects("node")) {
                Node n = (Node)g;
                if (n.contains(e.getX(), e.getY())) {
                    selectGraphical(n, true);
                    if (n.getClickLocation(e.getY()) == 0) {
                        System.out.println(n.getName());
                        editField = new JTextField(n.getName());
                        editField.setLocation(n.getUpperLeft().getX() + n.getWidth() / 2 - 15, n.getUpperLeft().getY());
                        editField.setSize(40, 20);
                        editField.addActionListener(new TextFieldListener(n, "name",model));
                    } else if (n.getClickLocation(e.getY()) == 1) {
                        System.out.println(n.getType());
                        editField = new JTextField(n.getType());
                        editField.setLocation(n.getUpperLeft().getX() + n.getWidth() / 2 - 15, n.getUpperLeft().getY() + n.getHeight() / 2);
                        editField.setSize(80, 20);
                        editField.addActionListener(new TextFieldListener(n, "type", model));
                    }
                    editField.requestFocus();
                    add(editField);
                }
            }
        }
        model.printSelection();
    }

    public void mousePressed(MouseEvent e) {
        System.out.println("MousePressed");
        this.dragStartPoint = e.getPoint();
        this.dragEndPoint = e.getPoint();
        if (model.getSelection("node").size() == 1 && !e.isControlDown()) {
            deSelect();
        }
        for (Graphical g : model.getObjects("handle")) {
                Handle h = (Handle)g;
            if (h.contains(e.getX(), e.getY())) {
                draggingHandle = true;
                if (h.getBase() != null) {
                    selectGraphical(h.getBase(), true);
                }
                selectGraphical(h, true);
                break;
            }
        }
        for (Graphical g : model.getObjects("node")) {
                Node n = (Node)g;
            if (n.contains(e.getX(), e.getY())) {
                draggingNode = true;
                selectGraphical(n, true);
                break;
            }
        }
        if (!draggingNode && !draggingHandle) {
            deSelect();
        }
        model.printSelection();
    }

    public void mouseReleased(MouseEvent e) {
        //deSelect();
        System.out.println("MouseReleased");
        if (dragRectangle != null) {
            for (Graphical g : model.getObjects("node")) {
                Node n = (Node)g;
                if (n.isContainedIn(dragRectangle)) {
                    selectGraphical(n, true);
                }
            }
            for (Graphical g : model.getObjects("handle")) {
                Handle h = (Handle)g;
                if (h.isContainedIn(dragRectangle) && h.getClass().getName().toString().equals("diagram.handles.TriggerHandle")) {
                    selectGraphical(h, true);
                }
            }
        }
        for (Graphical g : model.getObjects("node")) {
                Node n = (Node)g;
            if (n.contains(e.getX(), e.getY())) {
                selectGraphical(n, true);
                break;
            }
        }

        if (e.isPopupTrigger()) {
            popup.menuPanel(new AbsoluteLocation(e.getX(), e.getY()));
            boolean found = false;
            for (Graphical g : model.getObjects("node")) {
                Node n = (Node)g;
                boolean k = n.contains(e.getX(), e.getY());
                if (k) {
                    deSelect();
                    selectGraphical(n, true);
                    popup.menuNode();
                    found = true;
                }
            }
            if (!found) {
                for (Graphical g : model.getObjects("handle")) {
                Handle h = (Handle)g;
                    boolean k = h.contains(e.getX(), e.getY());

                    if (k) {
                        deSelect();
                        selectGraphical(h, true);
                        if (h.getClass().getName().toString().equals("diagram.handles.InputHandle")) {
                            popup.menuPort(new AbsoluteLocation(e.getX(), e.getY()), h);
                        } else {
                            popup.menuHandle(new AbsoluteLocation(e.getX(), e.getY()), h);
                        }
                        found = true;
                    }
                }
            }
            if (!found) {
                for (Graphical g : model.getObjects("connection")) {
                Connection c = (Connection)g;
                    boolean k = c.contains(e.getX(), e.getY());
                    if(k){
                        selectGraphical(c, true);
                        popup.menuConnection();
                        found = true;
                    }
                }

            }
        }
        this.draggingNode = false;
        this.draggingHandle = false;
        this.dragRectangle = null;
        model.printSelection();
    }

    public void mouseEntered(MouseEvent e) {
    }

    public void mouseExited(MouseEvent e) {
    }

    public void mouseMoved(MouseEvent e) {
        for (Graphical g : model.getObjects("handle")) {
                Handle h = (Handle)g;
            boolean isHContained = h.contains(e.getX(), e.getY());
            model.setHandleHighLighted(h, isHContained);
        }
    }

    public void mouseDragged(MouseEvent e) {
        System.out.println("MouseDragged");
        int x = e.getX(), y = e.getY();
        boolean handleDragged = false;
        if (this.draggingHandle) {
            for (Graphical g : model.getObjects("handle")) {
                Handle h = (Handle)g;
                if (h.isSelected()) {
                    int rx = x - dragEndPoint.x;
                    int ry = y - dragEndPoint.y;
                    h.drag(rx, ry);
                    
                    handleDragged = true;
                    if (model.getSelected().getClass().getName().equals("diagram.handles.ConnectHandle")) {
                        ConnectHandle ch = (ConnectHandle) model.getSelected();
                        System.out.println(ch!=null);
                        ch.AdjustLine(dragEndPoint.x, dragEndPoint.y);
                        for (Graphical hhg : model.getObjects("triggerhandle")) {
                            TriggerHandle gh = (TriggerHandle) hhg;
                            if (gh.contains(dragEndPoint.x, dragEndPoint.y)) {
                                model.setAttachable(true);
                                popup.menuAttach(ch, gh.getLocation());
                            }
                        }
                    }
                }
            }
        }

        if (!handleDragged) {
            if (this.draggingNode) {
                for (Graphical g : model.getObjects("node")) {
                Node n = (Node)g;
                    if (n.isSelected()) {
                        int xn = n.getX() + x - dragEndPoint.x;
                        int yn = n.getY() + y - dragEndPoint.y;
                        n.getUpperLeft().setXY(xn, yn);
                    }
                }
            } else {
                int xr = Math.min(dragStartPoint.x, x);
                int yr = Math.min(dragStartPoint.y, y);
                int wr = Math.abs(dragStartPoint.x - x);
                int hr = Math.abs(dragStartPoint.y - y);
                this.dragRectangle = new Rectangle(xr, yr, wr, hr);
            }
        }
//        if (model.getSelected() != null) {
//            if (model.getSelected().getClass().getName().equals("diagram.handles.ConnectHandle")) {
//                int rx = x - dragEndPoint.x;
//                int ry = y - dragEndPoint.y;
//                ConnectHandle ch = (ConnectHandle) model.getSelected();
//                ch.setConnectionTarget(new AbsoluteLocation(rx, ry));
//            }
//        }

        this.dragEndPoint = e.getPoint();
        this.repaint();
    }

    public void update(Observable o, Object arg) {
        if (editField != null) {
            editField.setVisible(false);
        }
        repaint();
    }
}